//==============================================================================
#include "GameClient.h"

string GameClient::DEFAULT_SERVER_HOST = "127.0.0.1";
string GameClient::DEFAULT_SERVER_PORT = "8080";
string GameClient::TEMP_PATH = ".\\temp\\";

//==============================================================================
GameClient::GameClient(string name, CLIENT_SESSION_TYPE type)
{
	setReceiver(new ClientReceiver(this));
	setSender(new ClientSender(this));
	setSocket(new ClientSocket(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT));
	
	setSessionType(type);
	setIsReady(false);
	setIsConnected(false);
	setName(name);
	setIsConnectionError(false);

	_isInitialized = false;
	_isSceneInitialized = false;
	
}
//------------------------------------------------------------------------------
GameClient::GameClient(string name, CLIENT_SESSION_TYPE type, string port)
{
	setReceiver(new ClientReceiver(this));
	setSender(new ClientSender(this));
	setSocket(new ClientSocket(DEFAULT_SERVER_HOST, port));

	setSessionType(type);
	setIsReady(false);
	setIsConnected(false);
	setName(name);
	setIsConnectionError(false);


	_isInitialized = false;
	_isSceneInitialized = false;

}
//------------------------------------------------------------------------------
GameClient::GameClient(string name, CLIENT_SESSION_TYPE type, string host, string port)
{
	setReceiver(new ClientReceiver(this));
	setSender(new ClientSender(this));
	setSocket(new ClientSocket(host, port));

	setSessionType(type);
	setIsReady(false);
	setIsConnected(false);
	setName(name);
	setIsConnectionError(false);

	_isInitialized = false;
	_isSceneInitialized = false;

}
//------------------------------------------------------------------------------
GameClient::~GameClient()
{
	delete getSocket();
	delete getSender();
	delete getReceiver();
}
//------------------------------------------------------------------------------
void GameClient::setIsSessionAccepted(bool flag)
{
	this->_isSessionAccepted = flag;
}
//------------------------------------------------------------------------------
bool GameClient::isSessionAccepted(void)
{
	return this->_isSessionAccepted;
}
//------------------------------------------------------------------------------
void GameClient::setSessionType(CLIENT_SESSION_TYPE type)
{
	this->sessionType = type;
}
//------------------------------------------------------------------------------
CLIENT_SESSION_TYPE GameClient::getSessionType(void)
{
	return this->sessionType;
}
//------------------------------------------------------------------------------
void GameClient::setSceneBackgroundPath(string path)
{
	this->sceneBackgroundPath = path;
}
//------------------------------------------------------------------------------
string GameClient::getSceneBackgroundPath(void)
{
	return this->sceneBackgroundPath;
}
//------------------------------------------------------------------------------
string GameClient::getTempPath()
{
	return this->TEMP_PATH;
}
//------------------------------------------------------------------------------
void GameClient::setIsConnected(bool flag)
{
	this->_isConnected = flag;
}
//------------------------------------------------------------------------------
bool GameClient::isConnected(void)
{
	return _isConnected;
}
//------------------------------------------------------------------------------
void GameClient::setIsReady(bool flag)
{
	this->_isReady = flag;
}
//------------------------------------------------------------------------------
bool GameClient::isReady(void)
{
	return _isReady;
}
//------------------------------------------------------------------------------
void GameClient::setIsConnectionError(bool flag){
	this->_isConnectionError = flag;
}
//------------------------------------------------------------------------------
bool GameClient::isConnectionError(){
	return this->_isConnectionError;
}
//------------------------------------------------------------------------------
void GameClient::setSocket(ClientSocket* socket)
{
	this->socket = socket;
}
//------------------------------------------------------------------------------
ClientSocket* GameClient::getSocket(void)
{
	return this->socket;
}
//------------------------------------------------------------------------------
void GameClient::setName(string name)
{
	this->name = name;
}
//------------------------------------------------------------------------------
string GameClient::getName(void)
{
	return name;
}
//------------------------------------------------------------------------------
CriticalSection* GameClient::getInputQueueLock()
{
	return &inputQueueLock;
}
//------------------------------------------------------------------------------
queue<Message*>* GameClient::getInputQueue()
{
	return &inputQueue;
}
//------------------------------------------------------------------------------
CriticalSection* GameClient::getOutputQueueLock()
{
	return &outputQueueLock;
}
//------------------------------------------------------------------------------
queue<Message*>* GameClient::getOutputQueue()
{
	return &outputQueue;
}
//------------------------------------------------------------------------------
void GameClient::setSender(ClientSender* sender)
{
	this->sender = sender;
}
//------------------------------------------------------------------------------
ClientSender* GameClient::getSender(void)
{
	return this->sender;
}
//------------------------------------------------------------------------------
void GameClient::setReceiver(ClientReceiver* receiver)
{
	this->receiver = receiver;
}
//------------------------------------------------------------------------------
ClientReceiver* GameClient::getReceiver()
{
	return this->receiver;
}
//------------------------------------------------------------------------------
DWORD GameClient::run(void)
{
	return start();
}
//------------------------------------------------------------------------------
int GameClient::start(void)
{
	Log::getInstance()->info("[CLIENT] Starting GameClient...");
	try
	{
		connectToServer();
		sendNewSessionRequest();
		if (isSessionAccepted())
		{
			setUpSession();
			while(waitForStartGame())
				Sleep(0);
			this->senderThread = ThreadPool::getInstance()->execute(getSender());
			this->receiverThread = ThreadPool::getInstance()->execute(getReceiver());
			setIsReady(true);
			this->senderThread->join(INFINITE);
			delete this->senderThread;
			this->receiverThread->join(INFINITE);
			delete this->receiverThread;
			//this->receiverThread->stop();
		}
		else
			throw NetworkException("Conextion was not Accepted!");
	}
	catch (NetworkException& ne)
	{
		Log::getInstance()->error("[CLIENT] Unable to start client");
	}
	return 0;
}
//------------------------------------------------------------------------------
void GameClient::shutdown(void)
{
	getReceiver()->stopReceivingUpdates();
	getSender()->stopSendingUpdates();
	getSocket()->doShutdown();
	getSocket()->doClose();
}
//------------------------------------------------------------------------------
void GameClient::connectToServer(void)
{
	Log::getInstance()->info("[CLIENT] Connecting to server...");
	if (!isConnected())
	{
		Log::getInstance()->info(socket->toString());
		try
		{
			getSocket()->doConnect();
			setIsConnected(true);
		}
		catch (NetworkException& ne)
		{
			Log::getInstance()->error("[CLIENT] Unable to connect to server: " + ne.getMessage());
			throw ne;
		}
	}
}
//------------------------------------------------------------------------------
void GameClient::sendNewSessionRequest(void)
{
	ByteBuffer sendBuffer, receiveBuffer;
	Log::getInstance()->info("[CLIENT] Sending NewSessionRequest...");
	Message newSessionMessage;
	switch(getSessionType())
	{
		case OBSERVER_CLIENT_SESSION:
			newSessionMessage.setType(NEW_OBSERVER_SESSION_REQUEST);
			break;
		case PLAYER_CLIENT_SESSION:
			newSessionMessage.setType(NEW_PLAYER_SESSION_REQUEST);
			break;
	}
	Log::getInstance()->info("[CLIENT] Sending GAME_FILES_REQUEST..");
	newSessionMessage.marshal(&sendBuffer);
	getSocket()->doSendComplete(&sendBuffer);
	
	getSocket()->doReceiveComplete(&receiveBuffer);
	Message severAnswerMessage (&receiveBuffer);
	if(severAnswerMessage.getType() == NEW_SESSION_REQUEST_ACCEPTED)
		setIsSessionAccepted(true);
	else
		setIsSessionAccepted(false);
}
//------------------------------------------------------------------------------
void GameClient::setUpSession(void)
{
	ByteBuffer sendBuffer;
	if (!isConnected())
		Log::getInstance()->error("[CLIENT] beginSession() >> Connection not stablished!");
	else
	{
		Message gameFileMessage (GAME_FILES_REQUEST, this->name);
		Log::getInstance()->info("[CLIENT] Sending GAME_FILES_REQUEST..");
		gameFileMessage.marshal(&sendBuffer);
		getSocket()->doSendComplete(&sendBuffer);
		receiveGameFiles();

		Log::getInstance()->info("[CLIENT] Beginning session...");
		Message sessionBeginMessage(SESSION_READY, this->name);
		Log::getInstance()->info("[CLIENT] Sending SESSION_READY..");
		sessionBeginMessage.marshal(&sendBuffer);
		getSocket()->doSendComplete(&sendBuffer);

		//Message* message = new Message(SCENE_STATE_REQUEST, this->name);
		//Log::getInstance()->info("[CLIENT] Sending SCENE_STATE_REQUEST..");
		//getSocket()->doSendComplete(message->marshal());
		//receiveSceneState();
	}
}
//------------------------------------------------------------------------------
bool GameClient::waitForStartGame(void)
{
	ByteBuffer buffer;
	getSocket()->doReceiveComplete(&buffer);
	Message message(&buffer);
	if(message.getType() != START_GAME)
		return true;
	else
		return false;
}
//------------------------------------------------------------------------------
void GameClient::endSession(void)
{
	Log::getInstance()->info("[CLIENT] Ending session...");
	Message* message = new Message(SESSION_END, "");
	Log::getInstance()->info("[CLIENT] Sending SESSION_END..");
	ByteBuffer sendBuffer;
	message->marshal(&sendBuffer);
	getSocket()->doSendComplete(&sendBuffer);
	delete message;
}
//------------------------------------------------------------------------------
void GameClient::receiveGameFiles(void)
{
	char recvbuf[2048];
    int recvbuflen = 2048;
	int receiveResult;
	string bytes = "";
	string command = "";
	string filename = "";
	string startFileCommand = "STARTFILE";
	string endFileCommand = "ENDFILE";
	ofstream file;
	string backgroundFile = TEMP_PATH;

	bool isSendingFile = false;
	int totalReceived = 0;

	receiveResult = socket->doReceive(recvbuf, recvbuflen);
	command = bytes.append(recvbuf).substr(0, startFileCommand.length());
	if (command.compare(startFileCommand) == 0)
	{
		cout << "Received STARTFILE command." <<  endl;
		receiveResult = socket->doReceive(recvbuf, recvbuflen);
		bytes = "";
		filename = bytes.append(recvbuf).substr(0, receiveResult);
		cout << "Received filename " << filename <<  endl;
		setSceneBackgroundPath(backgroundFile.append(filename));
		file.open(getSceneBackgroundPath(), ios::out | ios::trunc | ios::binary);
		if (file.is_open())
		{
			do
			{
				isSendingFile = true;
				receiveResult = socket->doReceive(recvbuf, recvbuflen);
				if (receiveResult > 0)
				{
					cout << "Received " << receiveResult << " bytes." << endl;
					totalReceived += receiveResult;

					bytes = "";
					command = bytes.append(recvbuf).substr(0, endFileCommand.length());
					if (command.compare(endFileCommand) == 0)
					{
						cout << "Received ENDFILE command." <<  endl;
						file.flush();
						file.close();
						isSendingFile = false;
						_isInitialized = true;
					}
					file.write(recvbuf, receiveResult);
				}
				else if (receiveResult == 0)
				{
					cout << "Connection closed" << endl;
				}
				else
				{
					cout << "recv failed with error: " << WSAGetLastError() << endl;
				}
			} while (receiveResult > 0 && isSendingFile);
		}
	}

	if (file.is_open())
	{
		cout << "There was an error that prevented the file to be closed." << endl;
	}
	cout << "Total received: " << totalReceived << endl;
}
//------------------------------------------------------------------------------
void GameClient::receiveSceneState(void)
{
	char recvbuf[1024];
    int recvbuflen = 1024;
	int receiveResult;
	char* pBuf;
	string bytes = "";
	string command = "";
	string startFileCommand = "SCENERECIBIDO";
	string endFileCommand = "ENDSCENE";
	ByteBuffer* buffer = new ByteBuffer();
	bool isSendingScene = false;
	int totalReceived = 0;
	int dataReceived =0;
	char bufferRead[1024];
	
	receiveResult = socket->doReceive(recvbuf, recvbuflen);
	//memcpy(sendBuffer, readBuffer+(totalBytesSent), bytesToSend);
	pBuf= recvbuf;
	int sceneSize = atoi(pBuf);
	cout << "Received scene size:" << sceneSize<<  endl;
	stringstream ss;
	ss << sceneSize;	
	int sizeOfSize=(int)strlen(ss.str().c_str());
	pBuf+= sizeOfSize;
	dataReceived = receiveResult-sizeOfSize;
	isSendingScene=true;
	if (dataReceived >0){
			buffer->put(pBuf, dataReceived);
			if ( (dataReceived) == sceneSize)
				isSendingScene=false;
	}
	while(receiveResult > 0 && isSendingScene ){
			receiveResult = socket->doReceive(recvbuf, recvbuflen);
			pBuf=recvbuf;
			dataReceived += receiveResult;
			buffer->put(pBuf, dataReceived);
			
		
		if ( (dataReceived) == sceneSize)
			isSendingScene=false;
	}
	//command = bytes.append(recvbuf).substr(0, startFileCommand.length());
/*
	if (command.compare(startFileCommand) == 0)
	{
		cout << "Received STARTSCENE command." <<  endl;
		do
			{
				isSendingScene = true;
				receiveResult = socket->doReceive(recvbuf, recvbuflen);
				if (receiveResult > 0)
				{
					cout << "Received " << receiveResult << " bytes." << endl;
					

					bytes = "";
					command = bytes.append(recvbuf).substr(0, endFileCommand.length());
					if (command.compare(endFileCommand) == 0)
					{
						cout << "Received ENDSCENE command." <<  endl;
						isSendingScene = false;
						_isSceneInitialized = true;
					}
					if(!_isSceneInitialized){
						buffer->put(recvbuf, receiveResult);
					totalReceived += receiveResult;
					}
				}
				else if (receiveResult == 0)
				{
					cout << "Connection closed" << endl;
				}
				else
				{
					cout << "recv failed with error: " << WSAGetLastError() << endl;
				}
			} while (receiveResult > 0 && isSendingScene);
		
	}*/
	cout << "Total received: " << dataReceived << endl;


	//char recvbuf[1024];
 //   int recvbuflen = 1024;
	//ByteBuffer* buffer = new ByteBuffer();
	//int receiveResult;
	//int totalReceived = 0;
	//bool isSendingFile = false;
	//receiveResult = socket->doReceive(recvbuf, recvbuflen);
	//if (receiveResult > 0)
 //   {
	//	buffer->put(recvbuf, receiveResult);
	//	cout << "Received " << receiveResult << " bytes." << endl;
//}
	this->sceneBuff = buffer;
	socket->doSend(startFileCommand.c_str());
		cout << "Sent SCENESTate confirmation" << endl;
	Sleep(200);
	}
//------------------------------------------------------------------------------
int GameClient::disconnectFromServer(void)
{
	socket->doShutdown();
	socket->doClose();
    return 0;
}
//------------------------------------------------------------------------------
bool GameClient::isInitialized(void)
{
	return _isInitialized;
}
//------------------------------------------------------------------------------
void GameClient::queueUpdate(Message* message)
{
	getOutputQueueLock()->adquire();
	getOutputQueue()->push(message);
	getOutputQueueLock()->release();
}
//------------------------------------------------------------------------------
Message* GameClient::unqueueUpdate(void)
{
    getInputQueueLock()->adquire();
    Message* message = getInputQueue()->front();
    getInputQueue()->pop();
    getInputQueueLock()->release();
    return message;
}
//------------------------------------------------------------------------------
bool GameClient::hasUpdates(void)
{
	return !getInputQueue()->empty();
}
//------------------------------------------------------------------------------
void GameClient::getInfo(struct addrinfo* result)
{
	int i = 1;
	struct addrinfo* ptr = NULL;
	string info = "";
	stringstream ss;
	for(ptr=result; ptr != NULL ;ptr=ptr->ai_next)
	{
		ss << ptr->ai_flags;
		ss.str();
		info.append("Flags: ").append(ss.str());
		ss.clear();
        info.append("; Family: ");
        switch (ptr->ai_family)
		{
            case AF_UNSPEC:
                info.append("Unspecified");
                break;
            case AF_INET:
                info.append("AF_INET (IPv4)");
                break;
            case AF_INET6:
                info.append("AF_INET6 (IPv6)");
                break;
            case AF_NETBIOS:
                info.append("AF_NETBIOS (NetBIOS)");
                break;
            default:
				ss << ptr->ai_family;
				info.append("Other: ").append(ss.str());
				ss.clear();
                break;
        }
        info.append("; Socket type: ");
        switch (ptr->ai_socktype) {
            case 0:
                info.append("Unspecified");
                break;
            case SOCK_STREAM:
                info.append("SOCK_STREAM (stream)");
                break;
            case SOCK_DGRAM:
                info.append("SOCK_DGRAM (datagram)");
                break;
            case SOCK_RAW:
                info.append("SOCK_RAW (raw)");
                break;
            case SOCK_RDM:
                info.append("SOCK_RDM (reliable message datagram)");
                break;
            case SOCK_SEQPACKET:
                info.append("SOCK_SEQPACKET (pseudo-stream packet)");
                break;
            default:
				ss << ptr->ai_socktype;
				info.append("Other: ").append(ss.str());
				ss.clear();
                break;
        }
        info.append("; Protocol: ");
        switch (ptr->ai_protocol) {
            case 0:
                info.append("Unspecified");
                break;
            case IPPROTO_TCP:
                info.append("IPPROTO_TCP (TCP)");
                break;
            case IPPROTO_UDP:
                info.append("IPPROTO_UDP (UDP)");
                break;
            default:
				ss << ptr->ai_protocol;
				info.append("Other: ").append(ss.str());
				ss.clear();
                break;
        }
		ss << ptr->ai_addrlen;
		info.append("; Length of this sockaddr: ").append(ss.str());
		ss.clear();
		if (ptr->ai_canonname != 0)
		{
			info.append("; Canonical name: ").append(ptr->ai_canonname);
		}
    }
	cout << "CLIENT INFO: " << info << endl;
}
//------------------------------------------------------------------------------
ByteBuffer* GameClient::getSceneBuff(){
	return this->sceneBuff;
}